///////////////////////////////////////////////////////////////////////////////
// $Id: testbench.v 1 2009-10-08 18:23:02Z root $
//
// Testbench: testbench
// Project: CPCI (PCI Control FPGA)
// Description: Tests the reg_file module
//
// Change history:
//
///////////////////////////////////////////////////////////////////////////////

`timescale 1 ns / 1 ns

module testbench ( );

// ==================================================================
// Constants
// ==================================================================

parameter Tpclk = 15;
parameter Tnclk = 8;
parameter Trst = 100;

reg pclk, nclk;
   
wire [31:0] AD;
wire  [3:0] CBE;
wire        PAR;
tri1        FRAME_N;
tri1        TRDY_N;
tri1        IRDY_N;
tri1        STOP_N;
tri1        DEVSEL_N;
tri1        INTR_A;
wire        RST_N;

// PCI ports from pci_top as generated by CoreGen
wire        IDSEL;
tri1        PERR_N;
tri1        SERR_N;
tri1        REQ_N;
tri1        GNT_N;

wire         cnet_reset;    // Reset signal to CNET
wire         phy_int_b;     // Interrupt signal from PHY

wire         cpci_rd_wr_L;  // Read/Write signal
wire         cpci_req;      // I/O request signal
wire [`CPCI_CNET_ADDR_WIDTH-1:0] cpci_addr;
wire [`CPCI_CNET_DATA_WIDTH-1:0] cpci_data;
wire         cpci_wr_rdy;   // Write ready from CNET
wire         cpci_rd_rdy;   // Read ready from CNET
wire [3:0]   cpci_tx_full;  // Indicates whether each MAC can
// accept a full-sized packet
wire         cnet_err;      // Error signal from CNET

wire [3:0]   cpci_dma_pkt_avail; // Packets waiting from MACs
wire [3:0]   cpci_dma_send; // Request next packet from MACs
wire         cpci_dma_wr_en; // Write stobe
wire [`CPCI_CNET_DATA_WIDTH-1:0] cpci_dma_data;
wire         cpci_dma_nearly_full;

// Reprogramming signals
wire         rp_cclk;
wire         rp_prog_b;
wire         rp_init_b;
wire         rp_cs_b;
wire         rp_rdwr_b;
wire [7:0]   rp_data;
wire         rp_done;

// Debug signals
wire         cpci_led;      
wire [31:0]  cpci_debug_data;
wire [1:0]   cpci_debug_clk;

// Reserved for future use
wire [19:0]  cpci_cnet_rsvd;

// ==================================================================
// Generate a clock signal
// ==================================================================

always
begin
   pclk <= 1'b1;
   #Tpclk;
   pclk <= 1'b0;
   #Tpclk;
end

always
begin
   nclk <= 1'b1;
   #Tnclk;
   nclk <= 1'b0;
   #Tnclk;
end

//******************************************************************//
// Generate a reset signal for use by the module.  The nominal      //
// duration of the initial reset is Trst.                           //
//******************************************************************//


reg           rst;

initial
begin
   rst <= 1'b1;
   #Trst;
   rst <= 1'b0;
end


// ==================================================================
// Instantiate the CPCI module
// ==================================================================

cpci_top cpci_top(
            .AD (AD),
            .CBE (CBE),
            .PAR (PAR),
            .FRAME_N (FRAME_N),
            .TRDY_N (TRDY_N),
            .IRDY_N (IRDY_N),
            .STOP_N (STOP_N),
            .DEVSEL_N (DEVSEL_N),
            .IDSEL (IDSEL),
            .INTR_A (INTR_A),
            .PERR_N (PERR_N),
            .SERR_N (SERR_N),
            .REQ_N (REQ_N),
            .GNT_N (GNT_N),
            .RST_N (RST_N),
            .PCLK (pclk),
            .nclk (nclk),
            .cnet_reset (cnet_reset),
            .phy_int_b (phy_int_b),
            .cpci_rd_wr_L (cpci_rd_wr_L),
            .cpci_req (cpci_req),
            .cpci_addr (cpci_addr),
            .cpci_data (cpci_data),
            .cpci_wr_rdy (cpci_wr_rdy),
            .cpci_rd_rdy (cpci_rd_rdy),
            .cpci_tx_full (cpci_tx_full),
            .cnet_err (cnet_err),
            .cpci_dma_pkt_avail (cpci_dma_pkt_avail),
            .cpci_dma_send (cpci_dma_send),
            .cpci_dma_wr_en (cpci_dma_wr_en),
            .cpci_dma_data (cpci_dma_data),
            .cpci_dma_nearly_full (cpci_dma_nearly_full),
            .rp_cclk (rp_cclk),
            .rp_prog_b (rp_prog_b),
            .rp_init_b (rp_init_b),
            .rp_cs_b (rp_cs_b),
            .rp_rdwr_b (rp_rdwr_b),
            .rp_data (rp_data),
            .rp_done (rp_done),
            .cpci_led (cpci_led),      
            .cpci_debug_data (cpci_debug_data),
            .cpci_debug_clk (cpci_debug_clk),
            .cpci_cnet_rsvd (cpci_cnet_rsvd)
          );

// ==================================================================
// Instantiate the host
// ==================================================================

host32 CPU (
            .AD (AD),
            .CBE (CBE),
            .PAR (PAR),
            .FRAME_N (FRAME_N),
            .TRDY_N (TRDY_N),
            .IRDY_N (IRDY_N),
            .STOP_N (STOP_N),
            .DEVSEL_N (DEVSEL_N),
            .INTR_A (INTR_A),
            .RST_N (RST_N),
            .CLK (pclk)
         );

// ==================================================================
// Instantiate other modules
// ==================================================================

cnet cnet(
            .rp_prog_b (rp_prog_b),
            .rp_init_b (rp_init_b),
            .rp_cs_b (rp_cs_b),
            .rp_rdwr_b (rp_rdwr_b),
            .rp_data (rp_data),
            .rp_done (rp_done),

            .want_crc_error (want_crc_error),

            .rp_cclk (rp_cclk)
         );

assign want_crc_error = 1'b0;

assign IDSEL = AD[16];
assign GNT_N = REQ_N;
assign RST_N = !rst;

reg [31:0] queue [15:0];
reg [3:0] rd_ptr, wr_ptr;
reg [4:0] depth;
reg [1:0] curr_byte;

reg [7:0] expected_byte;
reg write_prog_data;
wire [7:0] rp_data_reversed;

integer inactivity_count;

// Wait for the write to memory to be done
task wait_for_done;
   integer count;
   integer prev_depth;

   reg ok;

   begin
      ok = cpci_top.prog_done;
      prev_depth = depth;
      // Wait up to 20 clocks for the queue to deplete in depth
      count = 20;
      while (!ok) begin
         @(posedge pclk) ;
         ok = (cpci_top.prog_done || count == 0);
         count = count - 1;
         if (depth < prev_depth) begin
            count = 20;
            prev_depth = depth;
         end
      end
      if (!cpci_top.prog_done)
         $display($time, " ERROR: Not all programming data was sent to the CNET");
      else begin
         // Wait a few clock cycles for cnet_reprog to go low
         count = 20;
         ok = !cpci_top.cnet_reprog;
         while (!ok) begin
            @(posedge pclk) ;
            ok = !cpci_top.cnet_reprog;
         end
      end
   end
endtask

// When we see pci_idle_wait cycles of inactivity wait for programming to finish
parameter pci_idle_wait = 50;
always @(posedge pclk)
begin
   if (!FRAME_N || !IRDY_N || !TRDY_N || !DEVSEL_N) begin
      inactivity_count = 0;
      disable wait_for_done;
   end
   else begin
      inactivity_count = inactivity_count + 1;
      if (inactivity_count == pci_idle_wait)
         wait_for_done;
      if (inactivity_count > pci_idle_wait)
         $finish;
   end
end

always @(posedge pclk)
begin
   if (!RST_N) begin
      write_prog_data = 1'b0;
      rd_ptr = 'h0;
      wr_ptr = 'h0;
      depth = 'h0;
      curr_byte = 'h0;
   end
   else if (AD == {`CPCI_REPROG_DATA, 2'b0} && !FRAME_N) begin
      write_prog_data = 1'b1;
   end
   else if (!FRAME_N) begin
      write_prog_data = 1'b0;
   end
   else if (write_prog_data && !IRDY_N && !TRDY_N) begin
      $display("Found prog data");
      // Add the data to the queue
      queue[wr_ptr] = AD;
      wr_ptr = wr_ptr + 1;
      depth = depth + 1;
      write_prog_data = 1'b0;
   end
end

always @*
begin
   case (curr_byte)
      2'h0: expected_byte <= queue[rd_ptr][7:0];
      2'h1: expected_byte <= queue[rd_ptr][15:8];
      2'h2: expected_byte <= queue[rd_ptr][23:16];
      2'h3: expected_byte <= queue[rd_ptr][31:24];
   endcase
end

always @(posedge rp_cclk)
begin
   if (want_crc_error && !rp_cs_b) begin
      $display($time, " Wanting a CRC error");
      rd_ptr = wr_ptr;
      depth = 'h0;
   end
   else if (!rp_cs_b) begin
      if (depth == 0)
         $display($time, " ERROR: Unexpected %s to CNET of %x", rp_rdwr_b ? "READ" : "WRITE", rp_data_reversed);
      else begin
         if (rp_data_reversed == expected_byte)
            $display($time, " Success: Saw %s to CNET of %x", rp_rdwr_b ? "READ" : "WRITE", rp_data_reversed);
         else
            $display($time, " ERROR: Saw %s to CNET of %x, expected %x", rp_rdwr_b ? "READ" : "WRITE", rp_data_reversed, expected_byte);
         curr_byte = curr_byte + 1;
         if (curr_byte == 'h0) begin
            rd_ptr = rd_ptr + 1;
            depth = depth - 1;
         end
      end
   end
end

always @*
begin
   $display($time, " Flags: REPRG: %b   OVERFLOW: %b   ERR: %b   INIT: %b   DONE: %b  FIFO_EMPTY: %b", cpci_top.cnet_reprog , cpci_top.prog_overflow , cpci_top.prog_error , cpci_top.prog_init , cpci_top.prog_done, cpci_top.empty);
end

assign rp_data_reversed = {
            rp_data[0],
            rp_data[1],
            rp_data[2],
            rp_data[3],
            rp_data[4],
            rp_data[5],
            rp_data[6],
            rp_data[7]
            };

initial
   inactivity_count = 0;
//#1000 $finish;
endmodule // testbench

/* vim:set shiftwidth=3 softtabstop=3 expandtab: */
